After running the program of HSCI Simulation, we will get two files regarding trade info and backtesting data.
Then, we use this Jupyter Notebook for data visulization
This is part 1 of simulation. Part 2 (Performance Optimzation) will be handled by a seperate Jupyter Notebook
Overall, the result from hindsight seems to have potential to go further with good hit ratio. However, calendar rebalancing may not be optimal due to the relatively large drawdown. So, Part 2 (Performance Optimzation) is essential.
Configuration of HSCI Simulation could be modified in config.conf file.
Aggregated performance is using average approach.
Below are the major parameters used in this example. Also, halted stocks are excluded in performance calculation
"start_year": 2009,
"end_year": 2020,
"begin_business_day": 60,
"end_business_day": 30,
"funding_source": "2800 HK Equity"
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import performanceVisualization as pv # custom package
trade_df = pd.read_csv("hsci_trade_file.csv", low_memory=False)
backtest_df = pd.read_csv("hsci_backtest_file.csv", low_memory=False)
for df in [trade_df, backtest_df]:
for column in df.columns:
if "date" in column and column != "date_index":
df[column] = df[column].astype("datetime64[ns]")
trade_df.head()
backtest_df.head()
Most of the rebalanace cases are coming from Regular Index Review which held on March and September each year.
Add is far more than Delete in Regular Review, while Delete is the majority in Interim Review
1/3 of stocks in Interim Deletion are halted and 2/3 of them will be delisted within the period
IPO means the stocks was inactive in the original trade start date. So, it could be IPO or resume trading after being halted. These cases would have 0 IPO return, so I flaged it out.
1/2 of stocks in Interim Addition are IPO. Most of the cases are large stocks which had fast entry premit to the index.
count_summary = pv.get_count_summary(trade_df=trade_df)
count_summary
Addition Stocks in Regular HSCI Review tends to outperform our funding source (2800 HK Equity) (HSI)
Outperformance tends to happens from ~25 business day before effective date until ~10 business day after effective date.
Trends are similar no matter breaking down by year-month, year or month although some outliers exist
A hindsight summary will be generated based on manual input.
I picked -25D to +10D for the hindsight summary. Below are some key notes:
Per Trade Analysis
Parameters' optimization will be discussed in the Part 2
regular_add_chart = pv.get_aggregate_performance_chart(backtest_df, trade_df, review_type="Regular", change="Add")
regular_add_chart_year_month = pv.get_group_performance_chart(backtest_df, trade_df,
review_type="Regular", change="Add",
group_by="year_month")
regular_add_chart_year = pv.get_group_performance_chart(backtest_df, trade_df,
review_type="Regular", change="Add",
group_by="year")
regular_add_chart_month = pv.get_group_performance_chart(backtest_df, trade_df,
review_type="Regular", change="Add",
group_by="month")
hindsight_regular_add_backtest_df = pv.get_hindsight_backtest_df(backtest_df, trade_df,
review_type="Regular", change="Add",
begin_business_day=25, end_business_day=10)
hindsight_regular_add_chart = pv.get_aggregate_performance_chart(hindsight_regular_add_backtest_df,
trade_df, review_type="Regular", change="Add")
hindsight_regular_add_chart_year_month = pv.get_group_performance_chart(hindsight_regular_add_backtest_df,
trade_df,
review_type="Regular", change="Add",
group_by="year_month")
hindsight_regular_add_trade_summary = pv.get_trade_summary(hindsight_regular_add_backtest_df, trade_df)
hindsight_regular_add_trade_summary.T.rename(columns={0: "All Trade"})
hindsight_regular_add_trade_summary_year_month = pv.get_group_trade_summary(hindsight_regular_add_backtest_df,
trade_df, group_by="year_month")
hindsight_regular_add_trade_summary_year_month
Deletion Stocks in Regular HSCI Review tends to underperform our funding source (2800 HK Equity) (HSI)
Underperformance tends to since from ~40 business day. The underperformance trend could last long after effective date
it's different with some other indices, it seems there has no bounce back after the stocks have been deleted from HSCI.
Trends are similar no matter breaking down by year-month, year or month although some outliers exist
A hindsight summary will be generated based on manual input.
I picked -10D to +25D for the hindsight summary because -40D seems a bit early. Below are some key notes:
Per Trade Analysis
Parameters' optimization will be discussed in the Part 2 (Performance Optimization)
regular_delete_chart = pv.get_aggregate_performance_chart(backtest_df, trade_df,
review_type="Regular", change="Delete")
regular_delete_chart_year_month = pv.get_group_performance_chart(backtest_df, trade_df,
review_type="Regular", change="Delete",
group_by="year_month")
regular_delete_chart_year_month = pv.get_group_performance_chart(backtest_df, trade_df,
review_type="Regular", change="Delete",
group_by="year_month")
regular_delete_chart_year = pv.get_group_performance_chart(backtest_df, trade_df,
review_type="Regular", change="Delete",
group_by="year")
regular_delete_chart_month = pv.get_group_performance_chart(backtest_df, trade_df,
review_type="Regular", change="Delete",
group_by="month")
hindsight_regular_delete_backtest_df = pv.get_hindsight_backtest_df(backtest_df, trade_df,
review_type="Regular", change="Delete",
begin_business_day=10, end_business_day=25,
flip_side=True)
hindsight_regular_delete_chart = pv.get_aggregate_performance_chart(hindsight_regular_delete_backtest_df,
trade_df, review_type="Regular", change="Delete",
flip_side=True)
hindsight_regular_delete_chart_year_month = pv.get_group_performance_chart(hindsight_regular_delete_backtest_df,
trade_df,
review_type="Regular", change="Delete",
group_by="year_month")
hindsight_regular_delete_trade_summary = pv.get_trade_summary(hindsight_regular_delete_backtest_df, trade_df)
hindsight_regular_delete_trade_summary.T.rename(columns={0: "All Trade"})
hindsight_regular_delete_trade_summary_year_month = pv.get_group_trade_summary(hindsight_regular_delete_backtest_df,
trade_df, group_by="year_month")
hindsight_regular_delete_trade_summary_year_month
Interim Add with IPO is an interesting topic. The chart may be ugly as the numbers of stocks are not consitant in each date_index
This performance is exlcuding the IPO return
An Interesting fact is that the stocks which listed ~10 days before effective date tends to generate positive return even after IPO Date, although only ~15 cases in total.
A hindsight summary will be generated based on manual input.
I picked -10D to 0D for the hindsight summary. Below are some key points.
Per Trade Analysis
interim_add_ipo_chart = pv.get_aggregate_performance_chart(backtest_df, trade_df,
review_type="Interim", change="Add", ipo_only=True)
hindsight_interim_add_ipo_backtest_df = pv.get_hindsight_backtest_df(backtest_df, trade_df,
review_type="Interim", change="Add",
begin_business_day=10, end_business_day=0,
ipo_only=True)
hindsight_interim_add_ipo_chart = pv.get_aggregate_performance_chart(hindsight_interim_add_ipo_backtest_df,
trade_df, review_type="Interim", change="Add",
ipo_only=True)
hindsight_interim_add_ipo_chart_year_month = pv.get_group_performance_chart(hindsight_interim_add_ipo_backtest_df,
trade_df,
review_type="Interim", change="Add",
group_by="year_month", ipo_only=True)
hindsight_interim_add_ipo_trade_summary = pv.get_trade_summary(hindsight_interim_add_ipo_backtest_df, trade_df)
hindsight_interim_add_ipo_trade_summary.T.rename(columns={0: "All Trade"})
hingsight_ipo_return_chart = pv.get_return_scatter_plot(hindsight_interim_add_ipo_backtest_df, trade_df,
item="ipo_return")
Interim Deletion is hard to predict. A certain part of the deletions is due to Privatization which caused the upward return to the date before effective date.
Most of the stocks are delisted before effective date or even announcement date.
The huge drawdown after effective date are actually coming from a few stocks which are not delisted after effective date.
interim_delete_chart = pv.get_aggregate_performance_chart(backtest_df, trade_df,
review_type="Interim", change="Delete")